﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace PubSubGeneric
{
    public class SubscriptionCatalog : ISubscriptionCatalog
    {
        ConcurrentDictionary<string, HashSet<ISubscriptionInfo>> Subscribers = new ConcurrentDictionary<string, HashSet<ISubscriptionInfo>>();
        public bool Add(string topic, ISubscriptionInfo subscription)
        {
            HashSet<ISubscriptionInfo> topicSubscribers;
            if (!Subscribers.ContainsKey(topic))
                Subscribers[topic] = new HashSet<ISubscriptionInfo>();

            topicSubscribers = Subscribers[topic];

            if (topicSubscribers.Contains(subscription))
            {
                return true;
            }
            else
            {
                topicSubscribers.Add(subscription);
                if (OnSubscriptionAdded != null)
                    OnSubscriptionAdded(this, new SubscriptionEventArgs(subscription));
                return true;
            }
        }

        public bool Remove(string topic, ISubscriptionInfo subscription)
        {
            if (!Subscribers.ContainsKey(topic))
                return false;

            bool removed = false;
            removed = ((HashSet<ISubscriptionInfo>)GetTopicSubscribers(topic)).Remove(subscription);
            //ISubscriptionInfo Subscription = GetTopicSubscribers(topic).Where(p =5> p.Equals(subscription) ).FirstOrDefault();
            //removed = GetTopicSubscribers(topic).Remove(Subscription);
            if (removed && OnSubscriptionRemoved != null)
                OnSubscriptionRemoved(this, new SubscriptionEventArgs(subscription));

            return removed;
        }

        public bool Exists(string topic, ISubscriptionInfo subscription)
        {
            if (!Subscribers.ContainsKey(topic))
                return false;
            return Subscribers[topic].Any(p => p == subscription);
        }

        public IEnumerable<ISubscriptionInfo> GetTopicSubscribers(string topic)
        {
            return (Subscribers.ContainsKey(topic) ? Subscribers[topic] : new HashSet<ISubscriptionInfo>());
        }

        public IEnumerable<ISubscriptionInfo<T>> GetTopicSubscribers<T>(string topic)
        {
            return GetTopicSubscribers(topic).Where(p => (p as ISubscriptionInfo<T>) != null).Select(p => p as ISubscriptionInfo<T>);
        }

        public IEnumerable<ISubscriptionInfo<T>> GetTopicSubscribers<T>(string topic, T arg)
        {
            return GetTopicSubscribers<T>(topic)
                .Where(subscription => subscription.CompiledPredicate == null ||
                               subscription.CompiledPredicate(arg)).ToList();
        }

        #region IDisposable

        public void Dispose()
        {
            this.Subscribers = null;
            GC.SuppressFinalize(this);
        }

        #endregion


        public bool ClearCatalog()
        {
            this.Subscribers.Clear();
            return true;
        }

        public bool ClearTopicSubscribers(string topic)
        {
            foreach (var subscription in GetTopicSubscribers(topic))
            {
                this.Remove(topic, subscription);
            }
            return true;
        }

        public bool ClearTopicSubscribers<T>(string topic)
        {
            foreach (var subscription in GetTopicSubscribers<T>(topic))
            {
                this.Remove(topic, subscription);
            }
            return true;
        }
        public event SubscriptionAddedHandler OnSubscriptionAdded;

        public event SubscriptionRemovedHandler OnSubscriptionRemoved;


    }
}
